# -*- coding: utf-8 -*-
import socket
import numpy as np
import struct
import time
import pandas as pd
import matplotlib.pyplot as plt
import math
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense, Activation
from keras.callbacks import EarlyStopping
from keras.models import load_model
from keras.optimizers import SGD
#from hpelm import ELM
#from hpelm import HPELM
#from hpelm import modules

scaler = MinMaxScaler(feature_range=(0, 1))
model_name = 'model_on_10_off_20_count_3.h5'
recv_count = 0
lstm_model = load_model(model_name)
dataset = [0, 1, 2]  # save data for training
dataset.clear()
ann_model = 0  #:0 lstm 1:ann 2:elm
bRetrain = 0  #
print("*********1 Load keras begin  **********************")
from keras.models import load_model

print("*********  Load keras success**********************")

print("*********2 Load keras module  **********************")
new_model = load_model(model_name)
print("*********  Load keras success  ********************")

if (0 == ann_model):
    print("Machine Learning Algorithm: LSTM")
elif (1 == ann_model):
    print("Machine Learning Algorithm: ANN")
elif (2 == ann_model):
    print("Machine Learning Algorithm: ELM")
else:
    print("Machine Learning Algorithm: None")


def predict(x):
    xx = np.array([x])
    x_test = np.reshape(xx, (1, xx.shape[1], 1))

    y_predict = new_model.predict(x_test)
    res = y_predict.tolist()
    # print(x_test, y_predict)
    return res[0][0]


#print("*********3 Init server begin  **********************")
#sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#IPV4,TCP协议
#sock.bind(('127.0.0.1',50001))#绑定ip和端口，bind接受的是一个元组
#sock.listen(5)#设置监听，其值阻塞队列长度，一共可以有5+1个客户端和服务器连接

print("*********  Init server ok    **********************")


# ANN Model
def ANNModel(x_train, y_train, x_test, y_test):
    global bRetrain
    x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1]))
    x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1]))
    # build model
    model = Sequential()
    # model.add(Dense(3, activation='relu', input_dim= sequence_length))
    # model.add(Dropout(0.5))
    model.add(Dense(3, activation='relu', input_dim=x_train.shape[1]))
    # model.add(Dropout(0.5))
    model.add(Dense(2, activation='relu'))
    # model.add(Dropout(0.5))
    model.add(Dense(1, activation='linear'))
    # model.add(Dropout(0.5))
    # model.add(Dense(categorical_num, activation='softmax'))

    # training
    sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
    model.compile(loss='mse', optimizer=sgd, metrics=['accuracy'])
    early_stop = EarlyStopping(monitor='val_loss', patience=2)
    model.fit(x_train, y_train,
              epochs=30,
              batch_size=100,
              # validation_split = 0.1,
              validation_data=(x_test, y_test),
              callbacks=[early_stop])

    predict = model.predict(x_test)
    print(predict)
    bRetrain = 1
    return model

'''
# ELM Model
def ELMModel(x_train, y_train, x_test, y_test):
    global bRetrain
    x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1]))
    x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1]))
    model = ELM(x_train.shape[1], 1)
    model.add_neurons(20, "rbf_l2")
    model.add_neurons(14, "lin")
    model.train(x_train, y_train)
    predict_train = model.predict(x_train)
    predict_test = model.predict(x_test)
    # err_train = model.error(y_train,predict_train)
    # err_test = model.error(predict_test,y_test)

    bRetrain = 1
    return model
'''

# RNN Model
def RNNModel(x_train, y_train, x_test, y_test):
    global bRetrain
    x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], 1))
    x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], 1))

    model = Sequential()
    model.add(LSTM(input_shape=(None, 1), units=50, return_sequences=True))
    model.add(LSTM(100, return_sequences=False))
    model.add(Dense(units=1))
    model.add(Activation('linear'))

    model.compile(loss='mse', optimizer='rmsprop')
    early_stop = EarlyStopping(monitor='val_loss', patience=5)
    model.fit(x_train, y_train,
              batch_size=100, # old 100
              epochs=30, # old 30
              # validation_split = 0.2,
              validation_data=(x_test, y_test),
              callbacks=[early_stop])
    model.save(model_name)
    predict = model.predict(x_test)
    print(predict)
    bRetrain = 1
    return model


# build LSTM model
def build_model():
    model = Sequential()
    model.add(LSTM(input_shape=(None, 1), output_dim=50, return_sequences=True))
    model.add(LSTM(100, return_sequences=False))
    model.add(Dense(output_dim=1))
    model.add(Activation('linear'))

    model.compile(loss='mse', optimizer='rmsprop')
    return model


# train the model
def train_model(x_train, train_y, x_test, test_y):
    x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], 1))
    x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], 1))
    model = build_model()
    try:
        early_stop = EarlyStopping(monitor='val_loss', patience=5)
        model.fit(x_train, train_y, batch_size=50, epochs=100,
                  validation_split=0.2, callbacks=[early_stop])
        model.save(model_name)
        predict = model.predict(x_test)
        print(model.input.name)
        print(model.output.name)
        predict = np.reshape(predict, (predict.size,))
    except KeyboardInterrupt:
        print(predict)
        print(test_y)
    return model


# gen dataset
def gen_datases(datasets, sequence_length=10, split=0.8):
    datasets = np.array(datasets).astype(float)
    # datasets = datasets.reshape(len(datasets),1)
    data = []
    for i in range(len(datasets) - sequence_length - 1):
        data.append(datasets[i: i + sequence_length + 1])
    reshaped_data = np.array(data).astype('float64')
    # np.random.shuffle(reshaped_data)
    #
    x = reshaped_data[:, :-1]
    y = reshaped_data[:, -1]
    split_boundary = int(reshaped_data.shape[0] * split)
    train_x = x[: split_boundary]
    test_x = x[split_boundary:]
    # train_x = x
    # train_y = y
    # test_x = x
    # test_y = y

    train_y = y[: split_boundary]
    test_y = y[split_boundary:]
    return train_x, train_y, test_x, test_y


def trainning(datasets):
    global lstm_model
    global ann_model

    train_x, train_y, test_x, test_y = gen_datases(datasets)  # gen data
    if (1 == ann_model):
        lstm_model = ANNModel(train_x, train_y, test_x, test_y)  # train
    elif (0 == ann_model):
        lstm_model = RNNModel(train_x, train_y, test_x, test_y)  # train
    elif (2 == ann_model):
        # lstm_model = ELMModel(train_x, train_y, test_x, test_y)  # train
        lstm_model = RNNModel(train_x, train_y, test_x, test_y)  # train
    else:
        print("para errer")
    # do nothing
    return lstm_model


def predicting(model, x_predict):
    global bRetrain
    global ann_model
    x_predict = np.array([x_predict])

    # if(bRetrain & ann_model):
    if (bRetrain and (1 == ann_model)):
        x_test = np.reshape(x_predict, (1, x_predict.shape[1]))
    elif (bRetrain and (2 == ann_model)):
        x_test = np.reshape(x_predict, (1, x_predict.shape[1]))
    else:
        x_test = np.reshape(x_predict, (1, x_predict.shape[1], 1))
    y_predict = model.predict(x_test)
    res = y_predict.tolist()
    return res[0][0]


def addDataset(msgid, msg):
    global dataset
    global lstm_model
    # print(msgid)
    if (1024 != msgid):
        return
    if (len(dataset)):
        dataset.append(msg[-1])
    else:
        msg = list(msg)
        dataset = msg
    #if (len(dataset) >= 2*10000):
        #print("training the model ...... wait ........\n")
        #lstm_model = trainning(dataset)
        #dataset.clear()

sequence_length = 10
def saveData(test, predict, test_x, mse, acc):
    diff = predict - test
    npArray = np.hstack((test, predict, diff, test_x))
    strName = "./result" + "_" + str(sequence_length) + "_" + str(round(mse, 4)) + "_" + str(round(acc, 2)) + ".csv"
    np.savetxt(strName, npArray, fmt="%.3f", delimiter=",")

def MSE(y_predict, y_test):
    y_test = y_test.tolist()
    y_predict = y_predict.tolist()
    num = 0.0
    Size = len(y_test)
    for i in range(Size):
        d = abs(y_test[i][0] - y_predict[i][0])
        num = num + d * d
    mse = math.sqrt(num / Size)
    print(mse)
    return mse


def calAcc(y_predict, y_test):
    num = 0
    size = len(y_test)
    for i in range(size):
        tmp = int(y_predict[i] + 0.5)
        if tmp == y_test[i]:
            num = num + 1
    Acc = num / size
    print(Acc)
    return Acc

print("*********  \n * \n *\n        server is running now!!!!!!!!!!!!!!!!!!! \n**********************")

sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#IPV4,TCP协议
sock.bind(('127.0.0.1',60002))#绑定ip和端口，bind接受的是一个元组
sock.listen(5)#设置监听，其值阻塞队列长度，一共可以有5+1个客户端和服务器连接
onu_req = 0


predict_y = []
test_y = []

while True:
        #a=[1,2,3]
        #s1=str(a)# 将发送数据转化为String
        connection,address = sock.accept()# 等待客户请求
        onu_req += 1
        print("client ip is:",address,"onu_req is",onu_req)# 打印客户端地址


        data = connection.recv(2048)# 接收来自客户端的数据data
        #print(data) #打印收到的数据.decode('utf-8') b'\x01\x02\x03\x01\x02\x03\x01\x02'

        if not data:
            print("client has exist")
            break
        #buf 给data解码
        buf = struct.unpack("bbbbb", data) # uint8 输入的数据 P个周期  10个周期  (0, 0, 0, 0, 0, 0, 1, 1, 1, 1)

        test_y.append(buf[:])
        #提取部分buf为消息，进行预测
        msg = buf[:]

        #预测开始 ret为返回值
        addDataset(1024, msg)
        res = predicting(lstm_model, list(msg))
        if (res<=0):
            res = 0
        if (res > 100):
            res = 100
        ret = int(res + 0.5)
        print("predict value = %d \r\n", res)
        predict_y.append(res)
        recv_count += 1
        if (0 == (recv_count % 1000)):
            print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + "  num=" + str(recv_count))

        #预测后，发送编码的buffer给matlab,ret是预测包的个数值
        buffer = struct.pack("b", ret)  #预测Q个周期 此时为1
        connection.send(buffer)# 发送数据
        connection.close()# 关闭连接
    # 关闭服务器
sock.close()

